package cn.zk.base;

import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.KeeperException;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;

import java.util.Objects;
import java.util.concurrent.CountDownLatch;

/**
 * ClassName: ZkClientSecond
 * Desc: 基本API的使用
 * Date： 2016/12/5
 * Created：shaom
 */
public class ZkClientSecond implements Watcher {


    private static final CountDownLatch downLatch = new CountDownLatch(1);
    public static final String connectString = "master:2181,slave01:2181,slave02:2181";
//    public static final  String connectString = "master:2181,slave01:2181,slave02:2181/testNode";//可以添加根目录

    private static ZooKeeper zkClient;

    public ZkClientSecond() {
        try {
            getZkClient();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    void getZkClient() throws Exception {
        zkClient = Objects.isNull(zkClient) ? new ZooKeeper(connectString, 2000, this) : zkClient;
        downLatch.await();
    }

    /**
     * 不支持递归创建
     *
     * @throws Exception
     */
    void createNode() throws Exception {
        // Path must start with / character
        // 同步方法，还有异步方法
        // OPEN_ACL_UNSAFE: 不关心权限问题
        zkClient.create("/testNode/childNode2", "childValue".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }

    void createAynNode() throws Exception {
        // Path must start with / character
        // 同步方法，还有异步方法
        // OPEN_ACL_UNSAFE: 不关心权限问题
        zkClient.create("/testNode", "testValue".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

        // 临时节点
        zkClient.create("/testNode/childNode2", "testValue".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
    }


    void delNode() throws Exception {
        zkClient.delete("/testNode3", -1);
    }

    void getData() throws KeeperException, InterruptedException {
        System.out.println(new String(zkClient.getData("/testNode", this, null)));

        // 会触发 NodeDataChanged,值没有变，但是版本变化了。(监听的变化时值或者版本变化)
        zkClient.setData("/testNode", "change".getBytes(), -1);
    }

    void setNode() throws KeeperException, InterruptedException {
        // version 是数据版本。CAS（Compare and swap）理论。-1是基于最新的版本更新，非原子性
        Stat stat = zkClient.setData("/testNode", "change".getBytes(), -1);

        zkClient.setData("/testNode", "change".getBytes(), stat.getVersion());// 更好的进行原子操作

        zkClient.setData("/testNode", "change".getBytes(), stat.getVersion());// 失败
    }

    void getChild() throws KeeperException, InterruptedException {
        System.out.println(zkClient.getChildren("/testNode", true));// watcher /testNode这个节点，且是一次性的

        zkClient.create("/testNode/childNode", "testValue".getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);

        Thread.sleep(Integer.MAX_VALUE);
    }

    void exists() throws KeeperException, InterruptedException {

        // 对节点是否存在进行监听，一旦节点被创建、删除、数据更新，都会通知客户端
        Stat stat = zkClient.exists("/testNode", true);

        zkClient.setData("/testNode", "change".getBytes(), stat.getVersion());// NodeDataChanged
    }


    @Override
    public void process(WatchedEvent event) {
        System.out.println("状态 ： " + event.getState());
        System.out.println("path : " + event.getPath());
        System.out.println("已经触发了 " + event.getType() + " 事件！");

        if (Event.KeeperState.SyncConnected == event.getState()) {
            downLatch.countDown();

            switch (event.getType()) {
                case NodeCreated:
                    System.out.println("NodeCreated~~~");
                    break;
                case NodeDeleted:
                    System.out.println("NodeDeleted~~");
                    break;
                case NodeDataChanged:
                    System.out.println("NodeDataChanged~~");
                    break;
                case NodeChildrenChanged:
                    System.out.println("NodeChildrenChanged~~");
                    break;
                default:
                    System.out.println("default ~~~");
            }
        }
    }
}
